﻿#region Copyright (c) 2006-2011 Widgetsphere LLC, All Rights Reserved
//--------------------------------------------------------------------- *
//                          Widgetsphere  LLC                           *
//             Copyright (c) 2006-2011 All Rights reserved              *
//                                                                      *
//                                                                      *
//This file and its contents are protected by United States and         *
//International copyright laws.  Unauthorized reproduction and/or       *
//distribution of all or any portion of the code contained herein       *
//is strictly prohibited and will result in severe civil and criminal   *
//penalties.  Any violations of this copyright will be prosecuted       *
//to the fullest extent possible under law.                             *
//                                                                      *
//THE SOURCE CODE CONTAINED HEREIN AND IN RELATED FILES IS PROVIDED     *
//TO THE REGISTERED DEVELOPER FOR THE PURPOSES OF EDUCATION AND         *
//TROUBLESHOOTING. UNDER NO CIRCUMSTANCES MAY ANY PORTION OF THE SOURCE *
//CODE BE DISTRIBUTED, DISCLOSED OR OTHERWISE MADE AVAILABLE TO ANY     *
//THIRD PARTY WITHOUT THE EXPRESS WRITTEN CONSENT OF WIDGETSPHERE LLC   *
//                                                                      *
//UNDER NO CIRCUMSTANCES MAY THE SOURCE CODE BE USED IN WHOLE OR IN     *
//PART, AS THE BASIS FOR CREATING A PRODUCT THAT PROVIDES THE SAME, OR  *
//SUBSTANTIALLY THE SAME, FUNCTIONALITY AS ANY WIDGETSPHERE PRODUCT.    *
//                                                                      *
//THE REGISTERED DEVELOPER ACKNOWLEDGES THAT THIS SOURCE CODE           *
//CONTAINS VALUABLE AND PROPRIETARY TRADE SECRETS OF WIDGETSPHERE,      *
//INC.  THE REGISTERED DEVELOPER AGREES TO EXPEND EVERY EFFORT TO       *
//INSURE ITS CONFIDENTIALITY.                                           *
//                                                                      *
//THE END USER LICENSE AGREEMENT (EULA) ACCOMPANYING THE PRODUCT        *
//PERMITS THE REGISTERED DEVELOPER TO REDISTRIBUTE THE PRODUCT IN       *
//EXECUTABLE FORM ONLY IN SUPPORT OF APPLICATIONS WRITTEN USING         *
//THE PRODUCT.  IT DOES NOT PROVIDE ANY RIGHTS REGARDING THE            *
//SOURCE CODE CONTAINED HEREIN.                                         *
//                                                                      *
//THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE.              *
//--------------------------------------------------------------------- *
#endregion
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.IO;
using System.Globalization;
using System.Diagnostics;

using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;

using IServiceProvider = System.IServiceProvider;
using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider;
using VSStd97CmdID = Microsoft.VisualStudio.VSConstants.VSStd97CmdID;

using Widgetsphere.Generator.Common.Forms;
using Widgetsphere.Generator.Common.Util;
using Widgetsphere.Generator.Common.GeneratorFramework;
using Widgetsphere.VsCodeTools;
using EnvDTE;
using System.Collections;
using Widgetsphere.Generator.Common.EventArgs;
using System.Xml;
using System.ComponentModel.Design;

namespace Widgetsphere.VsCodeTools
{
	public partial class ModelViewer : System.Windows.Forms.UserControl,
		 IVsPersistDocData,
		 IPersistFileFormat,
		 IOleCommandTarget,
		//IVsFindTarget,
		 IVsWindowPane,
		 IVsDocDataFileChangeControl,
		 IVsFileChangeEvents,
		IEditor
	{
		#region Class Members

		private const string BADSAVEFLAGS = "Can not complete the save operation.";
		protected const string _myExtension = ".wsgen";

		private const uint _myFormat = 0;
		private ServiceProvider _vsServiceProvider = null;
		private Widgetsphere.VsCodeTools.VsCodeToolsPackage _myPackage = null;
		private IVsRunningDocumentTable _runningDocTable;

		// Flag true when we are loading the file. It is used to avoid to change the isDirty flag
		// when the changes are related to the load operation.
		private bool _loading;
		// This flag is true when we are asking the QueryEditQuerySave service if we can edit the
		// file. It is used to avoid to have more than one request queued.
		private bool _gettingCheckoutStatus;

		// Counter of the file system changes to ignore.
		private int _changesToIgnore;
		// Cookie for the subscription to the file system notification events.
		private uint _fileChangeNotifyCookie;
		// This flag is used to know when the reload procedure is running.
		private bool _isFileReloading;

		private Widgetsphere.Generator.Common.GeneratorFramework.IModelEditor _editor = null;
		private IDictionary<ModelUIAttribute, IModelEditor> _modelEditors = null;

		//private System.ComponentModel.Container _components = null;
		private System.Windows.Forms.Timer _reloadTimer;

		#endregion

		#region Class Constructors

		public ModelViewer()
			: base()
		{
			InitializeComponent();
			GeneratorStoreHelper.ProcessInstallations();
			try
			{

				_reloadTimer = new Timer();
				this.PrivateInit(null);

				this.CheckVersion();

				//If there is a generator store...
				if (GeneratorStoreHelper.IsStoreInstalled())
				{
					//Check if there are zero generators and if so show the gen store
					if (GeneratorStoreHelper.GetInstalledGenerators().Count() == 0)
					{
						UIHelper.ShowLibraryDialog();
					}
				}

			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
			}
		}

		public ModelViewer(Widgetsphere.VsCodeTools.VsCodeToolsPackage package)
			: this()
		{
			try
			{
				this.PrivateInit(package);
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
			}
		}

		#endregion

		#region Methods

		private void LoadControl(string loadingFile)
		{
			try
			{
				_modelEditors = GeneratorHelper.GetModelEditors(loadingFile);
				if (_modelEditors == null || _modelEditors.Count < 1)
					throw new Exception("Cannot find a model editor for file " + loadingFile + " Check first line of model file. It should look something like this <model guid=\"[SOMEGUID\" type=\"[SOMETYPE]\" assembly=\"[SOMETYPE]\" \\>");

				pnlEditorSelector.Visible = _modelEditors.Count != 1;

				foreach (KeyValuePair<ModelUIAttribute, IModelEditor> modelEditor in _modelEditors)
				{
					cbEditorSelection.Items.Add(modelEditor.Key);
				}
				cbEditorSelection.SelectedIndex = 0;

				((System.Windows.Forms.UserControl)_editor).Dock = DockStyle.Fill;
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
			}
		}

		private void CheckVersion()
		{
			//Check for latest version
			if (ServiceHelper.ShouldCheck())
			{
				if (ServiceHelper.CanConnect())
				{
					string version = ServiceHelper.GetLatestVersion();
					if (ServiceHelper.NeedUpdate(version))
					{
						if (AddinAppData.Instance.LastVersionChecked != version)
						{
							MessageBox.Show("The version of nHydrate you are using is " + ServiceHelper.GetCurrentVersion() + ". There is a newer version available " + ServiceHelper.GetLatestVersion() + ". Download the latest version at http://nHydrate.CodePlex.com.", "New Version Available", MessageBoxButtons.OK, MessageBoxIcon.Information);
							AddinAppData.Instance.LastVersionChecked = ServiceHelper.GetLatestVersion();
						}
					}
				}
				DateTime debugTime = AddinAppData.Instance.LastUpdateCheck;
				AddinAppData.Instance.LastUpdateCheck = DateTime.Now;
				AddinAppData.Instance.Save();
			}
		}

		#endregion

		#region IVsPersistDocData Members

		public int Close()
		{
			try
			{
				this.Dispose();
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
			}
			return VSConstants.S_OK;
		}

		public int GetGuidEditorType(out Guid pClassID)
		{
			int retVal = int.MinValue;
			try
			{
				retVal = ((IPersistFileFormat)this).GetClassID(out pClassID);
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
				pClassID = new Guid();
			}
			return retVal;
		}

		public int IsDocDataDirty(out int pfDirty)
		{
			int retVal = int.MinValue;
			try
			{
				retVal = ((IPersistFileFormat)this).IsDirty(out pfDirty);
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
				pfDirty = int.MinValue;
			}
			return retVal;
		}

		public int IsDocDataReloadable(out int pfReloadable)
		{
			try
			{
				pfReloadable = 1;
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
				pfReloadable = 1;
			}
			return VSConstants.S_OK;
		}

		public int LoadDocData(string pszMkDocument)
		{
			int retVal = int.MinValue;
			try
			{
				retVal = ((IPersistFileFormat)this).Load(pszMkDocument, 0, 0);
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
			}
			return retVal;
		}

		public int OnRegisterDocData(uint docCookie, IVsHierarchy pHierNew, uint itemidNew)
		{
			return VSConstants.S_OK;
		}

		public int ReloadDocData(uint grfFlags)
		{
			int retVal = int.MinValue;
			try
			{
				retVal = ((IPersistFileFormat)this).Load(null, grfFlags, 0);
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
			}
			return retVal;
		}

		public int RenameDocData(uint grfAttribs, IVsHierarchy pHierNew, uint itemidNew, string pszMkDocumentNew)
		{
			return VSConstants.S_OK;
		}

		private bool SaveDoc()
		{
			var queryEditQuerySave = (IVsQueryEditQuerySave2)GetVsService(typeof(SVsQueryEditQuerySave));

			// Call QueryEditQuerySave
			uint result = 0;
			int hr = queryEditQuerySave.QuerySaveFile(_editor.FileName, 0, null, out result);
			if (ErrorHandler.Failed(hr))
				return false;

			// Call the shell to do the save for us
			var uiShell = (IVsUIShell)GetVsService(typeof(SVsUIShell));
			string pbstrMkDocumentNew = null;
			int pfSaveCanceled = 0;
			hr = uiShell.SaveDocDataToFile(VSSAVEFLAGS.VSSAVE_SilentSave, (IPersistFileFormat)this, _editor.FileName, out pbstrMkDocumentNew, out pfSaveCanceled);
			if (ErrorHandler.Failed(hr))
				return false;

			return true;

		}

		public int SaveDocData(VSSAVEFLAGS dwSave, out string pbstrMkDocumentNew, out int pfSaveCanceled)
		{
			int hr = VSConstants.S_OK;
			try
			{
				pbstrMkDocumentNew = null;
				pfSaveCanceled = 0;

				switch (dwSave)
				{
					case VSSAVEFLAGS.VSSAVE_Save:
					case VSSAVEFLAGS.VSSAVE_SilentSave:
						{
							IVsQueryEditQuerySave2 queryEditQuerySave = (IVsQueryEditQuerySave2)GetVsService(typeof(SVsQueryEditQuerySave));

							// Call QueryEditQuerySave
							uint result = 0;
							hr = queryEditQuerySave.QuerySaveFile(_editor.FileName, 0, null, out result);
							if (ErrorHandler.Failed(hr))
								return hr;

							// Process according to result from QuerySave
							switch ((tagVSQuerySaveResult)result)
							{
								case tagVSQuerySaveResult.QSR_NoSave_Cancel:
									// Note that this is also case tagVSQuerySaveResult.QSR_NoSave_UserCanceled because these
									// two tags have the same value.
									pfSaveCanceled = ~0;
									break;

								case tagVSQuerySaveResult.QSR_SaveOK:
									{
										// Call the shell to do the save for us
										IVsUIShell uiShell = (IVsUIShell)GetVsService(typeof(SVsUIShell));
										hr = uiShell.SaveDocDataToFile(dwSave, (IPersistFileFormat)this, _editor.FileName, out pbstrMkDocumentNew, out pfSaveCanceled);
										if (ErrorHandler.Failed(hr))
											return hr;
									}
									break;

								case tagVSQuerySaveResult.QSR_ForceSaveAs:
									{
										// Call the shell to do the SaveAS for us
										IVsUIShell uiShell = (IVsUIShell)GetVsService(typeof(SVsUIShell));
										hr = uiShell.SaveDocDataToFile(VSSAVEFLAGS.VSSAVE_SaveAs, (IPersistFileFormat)this, _editor.FileName, out pbstrMkDocumentNew, out pfSaveCanceled);
										if (ErrorHandler.Failed(hr))
											return hr;
									}
									break;

								case tagVSQuerySaveResult.QSR_NoSave_Continue:
									// In this case there is nothing to do.
									break;

								default:
									throw new COMException("Unexpected error from the source control provider.");
							}
							break;
						}
					case VSSAVEFLAGS.VSSAVE_SaveAs:
					case VSSAVEFLAGS.VSSAVE_SaveCopyAs:
						{
							// Make sure the file name as the right extension
							if (string.Compare(_myExtension, System.IO.Path.GetExtension(_editor.FileName), true, CultureInfo.InvariantCulture) != 0)
							{
								_editor.FileName += _myExtension;
							}
							// Call the shell to do the save for us
							IVsUIShell uiShell = (IVsUIShell)GetVsService(typeof(SVsUIShell));
							hr = uiShell.SaveDocDataToFile(dwSave, (IPersistFileFormat)this, _editor.FileName, out pbstrMkDocumentNew, out pfSaveCanceled);
							if (ErrorHandler.Failed(hr))
								return hr;
							break;
						}
					default:
						throw new ArgumentException(BADSAVEFLAGS);
				}
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
				pbstrMkDocumentNew = string.Empty;
				pfSaveCanceled = 0;
			}
			return hr;

		}

		public int SetUntitledDocPath(string pszDocDataPath)
		{
			int retVal = int.MinValue;
			try
			{
				retVal = ((IPersistFileFormat)this).InitNew(_myFormat);
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
			}
			return retVal;
		}

		#endregion

		#region IPersistFileFormat Members

		private int _getClassIDRecurse = 0;
		public int GetClassID(out Guid pClassID)
		{
			_getClassIDRecurse++;
			try
			{
				if (_getClassIDRecurse < 10)
				{
					((Microsoft.VisualStudio.OLE.Interop.IPersist)this).GetClassID(out pClassID);
				}
				else
				{
					pClassID = new Guid();
				}
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
				pClassID = new Guid();
			}
			finally
			{
				_getClassIDRecurse--;
			}
			return VSConstants.S_OK;
		}

		public int GetCurFile(out string ppszFilename, out uint pnFormatIndex)
		{
			try
			{
				pnFormatIndex = _myFormat;
				ppszFilename = _editor.FileName;
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
				pnFormatIndex = 0;
				ppszFilename = string.Empty;
			}
			return VSConstants.S_OK;
		}

		public int GetFormatList(out string ppszFormatList)
		{
			try
			{
				ppszFormatList = "Model Editor files (*.wsgen) | *.wsgen";
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
				ppszFormatList = string.Empty;
			}
			return VSConstants.S_OK;
		}

		public int InitNew(uint nFormatIndex)
		{
			//DO NOTHING FOR NOW!
			return VSConstants.S_OK;

			try
			{
				if (nFormatIndex != _myFormat)
				{
					throw new ArgumentException("Unknown format.");
				}
				// until someone change the file, we can consider it not dirty as
				// the user would be annoyed if we prompt him to save an empty file
				_editor.IsDirty = false;
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
			}

			return VSConstants.S_OK;
		}

		public int IsDirty(out int pfIsDirty)
		{
			try
			{
				if (_editor != null && _editor.IsDirty)
				{
					pfIsDirty = 1;
				}
				else
				{
					pfIsDirty = 0;
				}
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
				pfIsDirty = 0;
			}
			return VSConstants.S_OK;
		}

		private static bool _nagScreenShown = false;
		public new int Load(string pszFilename, uint grfMode, int fReadOnly)
		{
			if ((pszFilename == null) && ((_editor.FileName == null) || (_editor.FileName.Length == 0)))
			{
				throw new ArgumentNullException("File name is invalid.");
			}

			//_loading = true;
			int hr = VSConstants.S_OK;
			LoadResultConstants loadResult = LoadResultConstants.Failed;
			try
			{
				bool isReload = false;

				// If the new file name is null, then this operation is a reload
				if (pszFilename == null)
				{
					isReload = true;
				}

				// Show the wait cursor while loading the file
				IVsUIShell vsUiShell = (IVsUIShell)GetVsService(typeof(SVsUIShell));
				if (vsUiShell != null)
				{
					// Note: we don't want to throw or exit if this call fails, so
					// don't check the return code.
					vsUiShell.SetWaitCursor();
				}

				// Set the new file name
				if (!isReload)
				{
					// Unsubscribe from the notification of the changes in the previous file.
					LoadControl(pszFilename);
					AdviseFileChange(false);
					_editor.FileName = pszFilename;
				}

				// Load the file
				loadResult = _editor.LoadFile(_editor.FileName);

				if (!_nagScreenShown && (AddinAppData.Instance.Key == null))
				{
					_nagScreenShown = true;
					GeneratorHelper.ShowNagScreen();
				}

				// Subscribe for the notification on file changes.
				if (!isReload)
				{
					AdviseFileChange(true);
				}

				// Notify the load or reload
				NotifyDocChanged();
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
			}
			finally
			{
				//_loading = false;
			}

			if (loadResult == LoadResultConstants.SuccessDirty)
			{
				//this.Generator.RootController.Object.Dirty = true;
				_editor.IsDirty = true;
				AdviseFileChange(true);
				NotifyDocChanged();
			}

			return hr;

		}

		public int Save(string pszFilename, int fRemember, uint nFormatIndex)
		{
			try
			{
				// We don't want to be notify for this change of the file.
				AdviseFileChange(false);

				// If file is null or same --> SAVE
				if (pszFilename == null || pszFilename == _editor.FileName)
				{
					_editor.SaveFile(_editor.FileName);
					_editor.IsDirty = false;
				}
				// If remember --> SaveAs
				else if (fRemember != 0)
				{
					_editor.FileName = pszFilename;
					_editor.SaveFile(_editor.FileName);
					_editor.IsDirty = false;
				}
				// Else, Save a Copy As
				else
				{
					_editor.SaveFile(pszFilename);
				}
				// Now that the file is saved (and maybe renamed) we can subscribe again
				// for the file system events.
				AdviseFileChange(true);
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
			}

			return VSConstants.S_OK;

		}

		public int SaveCompleted(string pszFilename)
		{
			return VSConstants.S_OK;
		}

		#endregion

		#region IOleCommandTarget Members
		public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
		{
			if (pguidCmdGroup == VSConstants.GUID_VSStandardCommandSet97)
			{
				// Process standard Visual Studio Commands
				switch (nCmdID)
				{
					case (uint)VSStd97CmdID.Exit:
					default: return (int)(Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED);
				}
			}
			else if (pguidCmdGroup == GuidList.guidVsCodeToolsCmdSet)
			{
				//Determine if this guy can generate
				if (!GeneratorHelper.CanGenerate())
				{
					MessageBox.Show("The logged-in user does not have permissions to run the nHydrate plugin. This is generally due to the UAC (user access control) being enabled. If you have this feature enabled, please disable it or run Visual Studio as an administrator.", "Invalid Permissions", MessageBoxButtons.OK, MessageBoxIcon.Error);
					return VSConstants.S_OK;
				}

				switch (nCmdID)
				{
					// if we had commands specific to our editor, they would be processed here
					case 0x100:

						if (File.Exists(_editor.FileName))
						{
							var fi = new FileInfo(_editor.FileName);
							if ((fi.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
							{
								MessageBox.Show("The model file is read-only. The file must be writable to proceed.", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
								return VSConstants.S_OK;
							}
						}

						this.SaveDoc();
						((Widgetsphere.Generator.Common.GeneratorFramework.IModelEditor)_editor).GenerateAll();
						break;
					case 0x103:
						_editor.Import();
						break;
					case 0x104:
						//ISelectionContainer propertyWindow = GetPropertiesWindow();
						//object[] a = new object[1];
						//var b = propertyWindow.GetObjects(0, 1, a);
						//var r = propertyWindow.SelectObjects(1, new object[] { new TreeNode() { Name = "qqq" } }, 0);
						//object[] c = new object[1];
						//var d = propertyWindow.GetObjects(0, 1, c);

						_editor.Verify();
						break;
					case 0x105:
						GeneratorHelper.ShowSettings();
						break;
					default:
						return (int)(Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED);
				}
			}
			else
				return (int)Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_UNKNOWNGROUP;

			return VSConstants.S_OK;

		}

		public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText)
		{
			if ((prgCmds == null))
				return VSConstants.E_INVALIDARG;


			if (pguidCmdGroup == VSConstants.GUID_VSStandardCommandSet97)
			{
				// Process standard Commands
				switch (prgCmds[0].cmdID)
				{
					case (uint)VSStd97CmdID.Exit:
					default: return (int)(Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED);
				}
			}
			else if (pguidCmdGroup == GuidList.guidVsCodeToolsCmdSet)
			{
				// Process our Commands
				switch (prgCmds[0].cmdID)
				{
					case 0x100:
						return (int)(Microsoft.VisualStudio.OLE.Interop.Constants.MSOCMDF_ENABLED);
					case 0x102:
						return (int)(Microsoft.VisualStudio.OLE.Interop.Constants.MSOCMDF_ENABLED);
					default:
						return (int)(Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED);
				}
			}
			else
				return (int)(Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED);

		}

		#endregion

		#region IVsWindowPane Members

		public int ClosePane()
		{
			try
			{
				this.Dispose(true);
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
			}
			return VSConstants.S_OK;
		}

		public int CreatePaneWindow(IntPtr hwndParent, int x, int y, int cx, int cy, out IntPtr hwnd)
		{
			try
			{
				Win32Methods.SetParent(Handle, hwndParent);
				hwnd = Handle;

				Size = new System.Drawing.Size(cx - x, cy - y);
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
				hwnd = new IntPtr();
			}
			return VSConstants.S_OK;
		}

		public int GetDefaultSize(SIZE[] pSize)
		{
			try
			{
				if (pSize.Length >= 1)
				{
					pSize[0].cx = Size.Width;
					pSize[0].cy = Size.Height;
				}
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
			}

			return VSConstants.S_OK;
		}

		public int LoadViewState(IStream pStream)
		{
			return VSConstants.S_OK;
		}

		public int SaveViewState(IStream pStream)
		{
			return VSConstants.S_OK;
		}

		public int SetSite(Microsoft.VisualStudio.OLE.Interop.IServiceProvider psp)
		{
			try
			{
				_vsServiceProvider = new ServiceProvider(psp);
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
			}
			return VSConstants.S_OK;
		}

		public int TranslateAccelerator(MSG[] lpmsg)
		{
			return VSConstants.S_FALSE;
		}

		#endregion

		#region IVsFindTarget Members
		/*
		public int Find(string pszSearch, uint grfOptions, int fResetStartPoint, IVsFindHelper pHelper, out uint pResult)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public int GetCapabilities(bool[] pfImage, uint[] pgrfOptions)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public int GetCurrentSpan(TextSpan[] pts)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public int GetFindState(out object ppunk)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public int GetMatchRect(RECT[] prc)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public int GetProperty(uint propid, out object pvar)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public int GetSearchImage(uint grfOptions, IVsTextSpanSet[] ppSpans, out IVsTextImage ppTextImage)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public int MarkSpan(TextSpan[] pts)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public int NavigateTo(TextSpan[] pts)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public int NotifyFindTarget(uint notification)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public int Replace(string pszSearch, string pszReplace, uint grfOptions, int fResetStartPoint, IVsFindHelper pHelper, out int pfReplaced)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public int SetFindState(object pUnk)
		{
			throw new Exception("The method or operation is not implemented.");
		}
		*/
		#endregion

		#region IVsDocDataFileChangeControl Members

		public int IgnoreFileChanges(int fIgnore)
		{
			try
			{
				if (0 != fIgnore)
				{
					++_changesToIgnore;
				}
				else
				{
					if (_changesToIgnore > 0)
					{
						--_changesToIgnore;
					}
				}
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
			}

			return VSConstants.S_OK;
		}

		#endregion

		#region IVsFileChangeEvents Members

		public int DirectoryChanged(string pszDirectory)
		{
			return VSConstants.S_OK;
		}

		public int FilesChanged(uint cChanges, string[] rgpszFile, uint[] rggrfChange)
		{
			try
			{			// Check the number of changes.
				if (0 == cChanges)
				{
					// Why this event was called if there is no change???
					return VSConstants.E_UNEXPECTED;
				}

				// If the counter of the changes to ignore (set by IVsDocDataFileChangeControl.IgnoreFileChanges)
				// is zero we can process this set of changes, otherwise ignore it.
				if (0 != _changesToIgnore)
					return VSConstants.S_OK;

				// Now scan the list of the changed files to find if the one opened in the editor
				// is one of the changed
				for (int i = 0; i < cChanges; ++i)
				{
					if (string.Compare(_editor.FileName, rgpszFile[i], true, CultureInfo.InvariantCulture) == 0)
					{
						// The file opened in the editor is changed.
						// The first step now is to find the kind of change.
						uint contentChange = (uint)_VSFILECHANGEFLAGS.VSFILECHG_Size | (uint)_VSFILECHANGEFLAGS.VSFILECHG_Time;
						if ((rggrfChange[i] & contentChange) != 0)
						{
							// The content of the file is changed outside the editor, so we need to
							// prompt the user for re-load the file. It is possible to have multiple
							// notification for this change, but we want to prompt the user and reload
							// the file only once
							if (_reloadTimer.Enabled || _isFileReloading)
							{
								// The reload is running, so we don't need to start it again.
								return VSConstants.S_OK;
							}

							// Set the timer timeout and start it
							_reloadTimer.Interval = 500;

							//REMOVE TIMER - DO NOT WANT THIS DIALOG POPUP ANYMORE
							//_reloadTimer.Enabled = true;
							//_reloadTimer.Start();
						}
					}
				}
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
			}

			return VSConstants.S_OK;
		}
		#endregion

		#region IEditor Implementation

		// Note that all functions implemented here call functions from the rich
		// edit control's text object model.

		/// <summary>
		/// This property gets/sets the default tab width.
		/// </summary>
		public float DefaultTabStop
		{
			get { return 0; }
			set { ; }
		}

		/// <summary>
		/// This property gets our editor's current ITextRange interface.  ITextRange is part
		/// of the rich edit control's text object model.
		/// </summary>
		public tom.ITextRange Range
		{
			get { return null; }
		}

		/// <summary>
		/// This property gets our editor's current ITextSelection interface.  ITextSelection
		/// is part of the rich edit control's text object model.
		/// </summary>
		public tom.ITextSelection Selection
		{
			get { return null; }
		}

		/// <summary>
		/// This property gets/sets the selection properties that contain certain information
		/// about our editor's current selection.
		/// </summary>
		public int SelectionProperties
		{
			get { return 0; }
			set { ; }
		}

		/// <summary>
		/// This function finds a string and returns the length of the matched string.
		/// Note that this function does not move the cursor to the string that it finds.
		/// </summary>
		/// <param name="textToFind"> The string that we want to look for.</param>
		/// <returns> The length of the matched string.</returns>
		public int FindText(string textToFind)
		{
			return 0;
		}

		/// <summary>
		/// This function has the same effect as typing the passed in string into the editor.
		/// Our implementation will just call TypeText since for now we want them both to do
		/// the same thing.
		/// </summary>
		/// <param name="textToSet"> The string to set/</param>
		/// <returns> HResult that indicates success/failure.</returns>
		public int SetText(string textToSet)
		{
			return 0;
		}

		/// <summary>
		/// This function has the same effect as typing the passed in string into the editor.
		/// </summary>
		/// <param name="textToType"> The string to type.</param>
		/// <returns> HResult that indicates success/failure.</returns>
		public int TypeText(string textToType)
		{
			return VSConstants.S_OK;
		}

		private static void addCommand(IMenuCommandService mcs, Guid menuGroup, int cmdID,
															 EventHandler commandEvent, EventHandler queryEvent)
		{
			// Create the OleMenuCommand from the menu group, command ID, and command event
			CommandID menuCommandID = new CommandID(menuGroup, cmdID);
			OleMenuCommand command = new OleMenuCommand(commandEvent, menuCommandID);

			// Add an event handler to BeforeQueryStatus if one was passed in
			if (null != queryEvent)
			{
				command.BeforeQueryStatus += queryEvent;
			}

			// Add the command using our IMenuCommandService instance
			mcs.AddCommand(command);
		}

		public void onCut(object sender, EventArgs e)
		{
			Cut();
		}

		public int Cut()
		{
			var ctrl = FindFocusedControl(this);
			if ((ctrl != null) && (ctrl is TextBox))
			{
				((TextBox)ctrl).Cut();
			}

			return VSConstants.S_OK;
		}

		public void onDelete(object sender, EventArgs e)
		{
			DeleteText();
		}

		public int DeleteText()
		{
			var ctrl = FindFocusedControl(this);
			if ((ctrl != null) && (ctrl is TextBox))
			{
				((TextBox)ctrl).SelectedText = string.Empty;
			}

			return VSConstants.S_OK;
		}

		private void onQueryCutOrDelete(object sender, EventArgs e)
		{
			OleMenuCommand command = (OleMenuCommand)sender;
			var ctrl = FindFocusedControl(this);
			if ((ctrl != null) && (ctrl is TextBox))
			{
				command.Enabled = (((TextBox)ctrl).SelectedText.Length > 0);
			}
		}

		/// <summary>
		/// This function performs the copy operation in the editor.
		/// </summary>
		/// <returns> HResult that indicates success/failure.</returns>
		private void onCopy(object sender, EventArgs e)
		{
			Copy();
		}

		public int Copy()
		{
			var ctrl = FindFocusedControl(this);
			if ((ctrl != null) && (ctrl is TextBox))
			{
				((TextBox)ctrl).Copy();
			}

			return VSConstants.S_OK;
		}

		private void onQueryCopy(object sender, EventArgs e)
		{
			OleMenuCommand command = (OleMenuCommand)sender;
			var ctrl = FindFocusedControl(this);
			if ((ctrl != null) && (ctrl is TextBox))
			{
				command.Enabled = (((TextBox)ctrl).SelectedText.Length > 0);
			}
		}

		/// <summary>
		/// This function performs the paste operation in the editor.
		/// </summary>
		/// <returns> HResult that indicates success/failure.</returns>
		public void onPaste(object sender, EventArgs e)
		{
			Paste();
		}

		public int Paste()
		{
			var ctrl = FindFocusedControl(this);
			if ((ctrl != null) && (ctrl is TextBox))
			{
				((TextBox)ctrl).Paste();
			}

			return VSConstants.S_OK;
		}

		private void onQueryPaste(object sender, EventArgs e)
		{
			OleMenuCommand command = (OleMenuCommand)sender;
			var ctrl = FindFocusedControl(this);
			if ((ctrl != null) && (ctrl is TextBox))
			{
				command.Enabled = true;
			}
		}

		private Control FindFocusedControl(Control control)
		{
			var container = control as ContainerControl;
			while (container != null)
			{
				control = container.ActiveControl;
				container = control as ContainerControl;
			}
			return control;
		}

		/// <summary>
		/// This function performs a delete in the editor.
		/// </summary>
		/// <param name="unit"> The type of units that we are going to delete.  The two valid options
		/// for this are TOMWord and TOMCharacter, which are defined in the TOMConstants enumeration.</param>
		/// <param name="count"> The number of units that we are going to delete.  Passing in a negative number
		/// will be similar to pressing backspace and passing in a positive number will be similar to
		/// pressing delete.</param>
		/// <returns> HResult that indicates success/failure.</returns>
		public int Delete(long unit, long count)
		{
			//editorControl.TextSelection.Delete((int)unit, (int)count);
			return VSConstants.S_OK;
		}

		/// <summary>
		/// This function will move up by the specified number of lines/paragraphs in the editor.
		/// </summary>
		/// <param name="unit"> The type of unit to move up by.  The two valid options for this are
		/// TOMLine and TOMParagraph, which are defined in the TOMConstants enumeration.</param>
		/// <param name="count"> The number of units to move.</param>
		/// <param name="extend"> This should be set to TOMExtend if we want to select as we move
		/// or TOMMove if we don't.  These values are defined in the TOMConstants enumeration.</param>
		/// <returns> The number of units that the cursor moved up.</returns>
		public int MoveUp(int unit, int count, int extend)
		{
			return 0;
		}

		/// <summary>
		/// This function will move down by the specified number of lines/paragraphs in the editor.
		/// </summary>
		/// <param name="unit"> The type of unit to move down by.  The two valid options for this are
		/// TOMLine and TOMParagraph, which are defined in the TOMConstants enumeration.</param>
		/// <param name="count"> The number of units to move.</param>
		/// <param name="extend"> This should be set to TOMExtend if we want to select as we move
		/// or TOMMove if we don't.  These values are defined in the TOMConstants enumeration.</param>
		/// <returns> The number of units that the cursor moved down.</returns>
		public int MoveDown(int unit, int count, int extend)
		{
			return 0;
		}

		/// <summary>
		/// This function will move to the left by the specified number of characters/words in the editor.
		/// </summary>
		/// <param name="unit"> The type of unit to move left by.  The two valid options for this are
		/// TOMWord and TOMCharacter, which are defined in the TOMConstants enumeration.</param>
		/// <param name="count"> The number of units to move.</param>
		/// <param name="extend"> This should be set to TOMExtend if we want to select as we move
		/// or TOMMove if we don't.  These values are defined in the TOMConstants enumeration.</param>
		/// <returns> The number of units that the cursor moved to the left.</returns>
		public int MoveLeft(int unit, int count, int extend)
		{
			return 0;
		}

		/// <summary>
		/// This function will move to the right by the specified number of characters/words in the editor.
		/// </summary>
		/// <param name="unit"> The type of unit to move right by.  The two valid options for this are
		/// TOMWord and TOMCharacter, which are defined in the TOMConstants enumeration.</param>
		/// <param name="count"> The number of units to move.</param>
		/// <param name="extend"> This should be set to TOMExtend if we want to select as we move
		/// or TOMMove if we don't.  These values are defined in the TOMConstants enumeration.</param>
		/// <returns> The number of units that the cursor moved to the right.</returns>
		public int MoveRight(int unit, int count, int extend)
		{
			return 0;
		}

		/// <summary>
		/// This function will either move the cursor to either the end of the current line or the end of the document.
		/// </summary>
		/// <param name="unit"> If this value is equal to TOMLine it will move the cursor to the end of the line.  If
		/// it is set to TOMStory then it will move to the end of the document.  These values are defined in the
		/// TOMConstants enumeration.</param>
		/// <param name="extend"> This should be set to TOMExtend if we want to select as we move
		/// or TOMMove if we don't.  These values are defined in the TOMConstants enumeration.</param>
		/// <returns> The number of characters that the operation moved the cursor by.  This value
		/// should always be positive since we are moving "forward" in the text buffer.</returns>
		public int EndKey(int unit, int extend)
		{
			return 0;
		}

		/// <summary>
		/// This function will either move the cursor to either the beggining of the current line or
		/// the beggining of the document.
		/// </summary>
		/// <param name="unit"> If this value is equal to TOMLine it will move the cursor to the beggining of the line.
		/// If it is set to TOMStory then it will move to the beggining of the document.  These values are defined in the
		/// TOMConstants enumeration.</param>
		/// <param name="extend"> This should be set to TOMExtend if we want to select as we move
		/// or TOMMove if we don't.  These values are defined in the TOMConstants enumeration.</param>
		/// <returns> The number of characters that the operation moved the cursor by.  This value
		/// should always be negative since we are moving "backward" in the text buffer.</returns>
		public int HomeKey(int unit, int extend)
		{
			return 0;
		}

		#endregion

		#region helper methods
		private void PrivateInit(Widgetsphere.VsCodeTools.VsCodeToolsPackage package)
		{
			try
			{
				_myPackage = package;
				_loading = false;
				_gettingCheckoutStatus = false;
				_isFileReloading = false;
				_fileChangeNotifyCookie = VSConstants.VSCOOKIE_NIL;
				_changesToIgnore = 0;

				Control.CheckForIllegalCrossThreadCalls = false;

				IMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as IMenuCommandService;
				if (null != mcs)
				{
					addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.Copy,
									new EventHandler(onCopy), new EventHandler(onQueryCopy));
					addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.Cut,
													new EventHandler(onCut), new EventHandler(onQueryCutOrDelete));
					addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.Paste,
													new EventHandler(onPaste), new EventHandler(onQueryPaste));
					addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int)VSConstants.VSStd97CmdID.Delete,
													new EventHandler(onDelete), new EventHandler(onQueryCutOrDelete));
				}

			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
			}
		}

		private object GetVsService(Type serviceType)
		{
			if (_vsServiceProvider == null) return null;
			else return _vsServiceProvider.GetService(serviceType);
		}

		private void AdviseFileChange(bool subscribe)
		{
			try
			{
				// Get the VsFileChangeEx service; this service will call us back when a file system
				// event will occur on the file(s) that we register.
				IVsFileChangeEx fileChangeService = (IVsFileChangeEx)GetVsService(typeof(SVsFileChangeEx));
				int hr = VSConstants.S_OK;

				if (!subscribe)
				{
					// If the goal is to unsubscribe, but there is no subscription active, exit.
					if (_fileChangeNotifyCookie == VSConstants.VSCOOKIE_NIL)
						return;

					// Now there is an active subscription, so unsubscribe.
					hr = fileChangeService.UnadviseFileChange(_fileChangeNotifyCookie);
					// No more subscription active, so set the cookie to NIL
					_fileChangeNotifyCookie = VSConstants.VSCOOKIE_NIL;
					ErrorHandler.ThrowOnFailure(hr);
					return;
				}

				// Here we want to subscribe for notification when the file opened in the editor changes.
				uint eventsToSubscribe = (uint)_VSFILECHANGEFLAGS.VSFILECHG_Size | (uint)_VSFILECHANGEFLAGS.VSFILECHG_Time;
				hr = fileChangeService.AdviseFileChange(
															_editor.FileName,                        // The file to check
															eventsToSubscribe,                // Filter to use for the notification
															(IVsFileChangeEvents)this,        // The callback to call
															out _fileChangeNotifyCookie);    // Cookie used to identify this subscription.
				ErrorHandler.ThrowOnFailure(hr);
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
			}
		}

		private void NotifyDocChanged()
		{
			try
			{// Get a reference to the Running Document Table
				if (null == _runningDocTable)
				{
					_runningDocTable = (IVsRunningDocumentTable)GetVsService(typeof(SVsRunningDocumentTable));
				}

				// Lock the document
				uint docCookie;
				IVsHierarchy hierarchy;
				uint itemID;
				IntPtr docData;
				int hr = _runningDocTable.FindAndLockDocument(
															(uint)_VSRDTFLAGS.RDT_ReadLock,
															_editor.FileName,
															out hierarchy,
															out itemID,
															out docData,
															out docCookie
				);
				ErrorHandler.ThrowOnFailure(hr);

				// Send the notification. Note that we do not throw now in case of error because before
				// we have to unlock the document.
				hr = _runningDocTable.NotifyDocumentChanged(docCookie, (uint)__VSRDTATTRIB.RDTA_DocDataReloaded);

				// Unlock the document.
				// Note that we have to unlock the document even if the previous call failed.
				_runningDocTable.UnlockDocument((uint)_VSRDTFLAGS.RDT_ReadLock, docCookie);

				// Check if the call to NotifyDocChanged failed.
				ErrorHandler.ThrowOnFailure(hr);
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
			}
		}

		private void _reloadTimer_Tick(object sender, System.EventArgs e)
		{
			try
			{
				// Here we want to check if we can process the reload.
				// We don't want to show the reload popup when the shell is not the active window,
				// so as first we check if the shell has the focus.
				// To do so we use the Win32 function GetActiveWindow that will return the handle
				// of the active window in the current application and NULL (0) if no window has
				// the focus.
				if (0 != NativeMethods.GetActiveWindow())
				{
					try
					{
						// Set the flag about the reload status. 
						// It is important to avoid that the popup will show multiple times for the same file.
						_isFileReloading = true;

						// The timer was used only to not run this procedure in syncronously inside the
						// notification function, so now we can stop end disable it.
						_reloadTimer.Stop();
						_reloadTimer.Enabled = false;

						// Build the title and message for the popup.
						string message = _editor.FileName + "\n\n" + ((Widgetsphere.Generator.Common.GeneratorFramework.IModelEditor)_editor).OutsideEditorFileChangeMessage;
						string title = "Microsoft Visual Studio";
						// Show the popup
						System.Windows.Forms.DialogResult res = MessageBox.Show(
																	this,
																	message,
																	title,
																	System.Windows.Forms.MessageBoxButtons.YesNo,
																	System.Windows.Forms.MessageBoxIcon.Question);
						if (res == System.Windows.Forms.DialogResult.Yes)
						{
							// The user wants to reload the data, so let's call the function that will do the job.
							int hr = ((IVsPersistDocData)this).ReloadDocData(0);
						}
					}
					catch (Exception ex)
					{
						//Do Nothing
					}
					finally
					{
						// Reset the flag about the reload status.
						_isFileReloading = false;
					}
				}
			}
			catch (ObjectDisposedException ex)
			{
				//This was throwing an error when saving the WSGEN file.
				//I have just disabled it since there is nothing that I can do
				//throw ex;
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
			}

		}

		internal static class NativeMethods
		{
			// We need this Win32 function to find out if the shell has the focus.
			[DllImport("user32.Dll")]
			static public extern int GetActiveWindow();
		}

		private System.Reflection.Assembly GetModelAssembly()
		{
			FileInfo fi = new FileInfo(System.Reflection.Assembly.GetExecutingAssembly().Location);

			//Get the configuration file if one exists
			var configFile = Path.Combine(fi.DirectoryName, "model.config");
			var defaultModeler = string.Empty;
			if (File.Exists(configFile))
			{
				try
				{
					XmlDocument document = new XmlDocument();
					document.Load(configFile);
					var node = document.DocumentElement.SelectSingleNode("//modelconfiguration/assembly");
					if (node != null)
					{
						defaultModeler = XmlHelper.GetAttributeValue(node, "path", string.Empty);
					}
				}
				catch (Exception ex)
				{
					//Do Nothing
				}
			}

			if (string.IsNullOrEmpty(defaultModeler))
				defaultModeler = Path.Combine(fi.DirectoryName, "Widgetsphere.ModelUI.dll");

			if (!File.Exists(defaultModeler))
			{
				MessageBox.Show("The modeler assembly cannot be found!\n" + defaultModeler, "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
				return null;
			}

			var assembly = System.Reflection.Assembly.LoadFrom(defaultModeler);
			return assembly;
		}
		#endregion

		#region Generate Methods

		private ISelectionContainer GetPropertiesWindow()
		{
			try
			{
				foreach (Window w in EnvDTEHelper.Instance.ApplicationObject.Windows)
				{
					if (w.Caption == "Properties")
					{
						return (ISelectionContainer)w;
					}
				}
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
			}
			return null;
		}

		private IVsOutputWindowPane CreatePane(Guid paneGuid, string title, bool visible, bool clearWithSolution)
		{
			IVsOutputWindowPane pane = null;
			try
			{
				IVsOutputWindow output = null;
				foreach (Window w in EnvDTEHelper.Instance.ApplicationObject.Windows)
				{
					if (w.Caption == "Output")
					{
						output = (IVsOutputWindow)w.Object;
						break;
					}
				}

				// Create a new pane.
				output.CreatePane(
															ref paneGuid,
															title,
															Convert.ToInt32(visible),
															Convert.ToInt32(clearWithSolution));

				// Retrieve the new pane.
				output.GetPane(ref paneGuid, out pane);
			}
			catch (Exception ex)
			{
				GlobalHelper.ShowError(ex);
			}
			return pane;
		}

		#endregion

		private void cbEditorSelection_SelectedIndexChanged(object sender, EventArgs e)
		{
			string fileName = null;
			if (_editor != null)
			{
				fileName = _editor.FileName;
				_editor.SaveFile(fileName);
			}

			_editor = _modelEditors[(ModelUIAttribute)cbEditorSelection.SelectedItem];
			pnlEditor.Controls.Clear();
			pnlEditor.Controls.Add((System.Windows.Forms.UserControl)_editor);
			((System.Windows.Forms.UserControl)_editor).Dock = DockStyle.Fill;

			if (!string.IsNullOrEmpty(fileName))
			{
				_editor.FileName = fileName;
				_editor.LoadFile(fileName);
			}

		}

	}
}